package omg.SongHandling.DataHandling;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import omg.Error.NoConnectionError;
import omg.Error.ParserCreationFailedError;
import omg.Error.ParsingFailedError;

abstract public class Parser {
        protected String type;
        protected URI uri; // the uniform ressource locator pointing to the audio source

        /**
	 * Creates a new parser object, ready to parse the provided url for
         * audio data and meta data.
         * @throws NoConnectionError Raised if no connection to the other URL
         * could be established.
         * @throws ParserCreationFailedError Raised when the Parser doesn't
         * consider itself responsible for the provided URL - to avoid this,
         * check with the static {@link #isResponsibleFor(URL)}.
         * @param uri the uniform ressource id pointing to the audio source
	 */
	public Parser(URI uri) throws ParserCreationFailedError,
                NoConnectionError {
            try {
                HttpURLConnection urlConn = (HttpURLConnection)
                uri.toURL().openConnection();
                urlConn.connect();
                if (HttpURLConnection.HTTP_OK != urlConn.getResponseCode())
                    throw new NoConnectionError();
            } catch (IOException e) {
                throw new NoConnectionError();
	    }
            this.uri = uri;
	}

	/**
	 * Checks whether an URL can be parsed by this Parser realization.
         * @param uri the uniform ressource id pointing to the audio source
	 * @return true, if the parse realizations considers itself responsible
         * for the supplied URI, otherwise false
	 */
	public static boolean isResponsibleFor(URI uri) {
            throw new UnsupportedOperationException();
	}

	/**
	 * Parse the source for links pointing to audio sources.
         * @throws ParsingFailedError Raised if some error occured during
         * parsing the source.
         * @throws NoConnectionError Raised if no connection to the other URL
         * could be established.
         * @return all found links as strings
	 */
	public abstract String[] parse () throws ParsingFailedError,
            NoConnectionError;

	/**
	 * Parse the source for metadata associated with the parsed links. The
         * index is respective to the ones returned by the {@link #parse()}
         * method.
         * @throws ParsingFailedError Raised if some error occured during
         * parsing the source.
         * @throws NoConnectionError Raised if no connection to the other URL
         * could be established.
	 * @return the metadata found for each audio source in structure title,
         * artist,album,genre,year
	 */
	public abstract String[][] getMetadata () throws ParsingFailedError,
            NoConnectionError;

        /**
	 * @return the source link associated with the parser object
	 */
	public URI getLink() {
		return this.uri;
	}

        /**
         * Helper function for developing additional parsers. Parses a input
         * stream line by line for a pattern and returns all hits.
         * @throws IOException Raised if an error with the supplied input stream
         * occures.
         * @param pattern the pattern to search for, the values of the first
         * pattern group (denoted by round braces) gets collected
         * @param stream the stream to search for the pattern
         * @return a string array holding all hits
         */
	protected static String[] parseFor (Pattern pattern, InputStream stream)
                throws IOException {
            String line;
            List<String> result = new ArrayList<String>();
            BufferedReader in = new BufferedReader(
                    new InputStreamReader(stream));
            try {
                while ((line = in.readLine()) != null) {
                    Matcher fit = pattern.matcher(line);
                    if (fit.find())
                        result.add(fit.group(1));
                }
            } finally {
                in.close();
            }
            return result.toArray(new String[]{});
	}

        public String getType() {
            return this.type;
        }
}
